/*
 * @Author: licongwei
 * @Date: 2022-02-11 16:49:02
 * @Description: 工具函数
 */

import { de } from 'element-plus/lib/locale'
import { IObj } from './types'
/**
 * @name: isObject
 * @description: 判断是否是对象类型
 * @param {T} value
 * @return {boolean}
 * @example: isObject(value)
 */
export function isObject<T>(value: T): boolean {
  const valueType = typeof value
  return value !== null && (valueType === 'object' || valueType === 'function')
}

/**
 * @name: toRawType
 * @description: 判断数据类型
 * @param {T} value 原始数据
 * @return {string}
 * @example: toRawType("abc")
 */
export function toRawType<T>(value: T): string {
  const _toString = Object.prototype.toString
  return _toString.call(value).slice(8, -1)
}

/**
 * @name: chartFontSize
 * @description: 以1920为基准实现图表字号大小自适应
 * @param {number} num 字号（px）
 * @return {number | undefined}
 * @example: chartFontSize(16)
 */
export function chartFontSize(num: number): number | undefined {
  const HEIGHT = 1080
  //获取屏幕宽度
  const clientHeight =
    window.innerHeight ||
    document.documentElement.clientHeight ||
    document.body.clientHeight
  return clientHeight && (num * clientHeight) / HEIGHT
}

/**
 * @name: gradientColor
 * @description: echarts渐变色方法，将颜色数组转换为渐变色并设置渐变方向
 *               (此方法只支持线向渐变，如需要径向渐变或纹理填充在完善)
 * @param {string[]} colors    颜色数组
 * @param {'top' | 'right' | 'bottom' | 'left'} direction 渐变方向（默认top）
 * @return {*}
 * @example: gradientColor(["#fff", "#000"], 'top')
 */
export function gradientColor(
  colors: string[] = ['#fff', '#000'],
  direction: 'top' | 'right' | 'bottom' | 'left' = 'top'
): IObj {
  // 根据颜色数组长度动态填充colorStops区间
  function setColorStops(colors: string[]) {
    const arr = []
    for (const index of colors.keys()) {
      const obj = {
        offset:
          index === colors.length - 1
            ? 1
            : (1 / Number(colors.length - 1)) * index,
        color: colors[index]
      }
      arr.push(obj)
    }
    return arr
  }
  return {
    type: 'linear',
    x: direction === 'left' ? 1 : 0,
    y: direction === 'top' ? 1 : 0,
    x2: direction === 'right' ? 1 : 0,
    y2: direction === 'bottom' ? 1 : 0,
    colorStops: setColorStops(colors),
    global: false
  }
}

/**
 * @name: merge
 * @description: 深合并echarts配置项
 * @param {IObj} optMap 全局配置项
 * @param {IObj} optProp 传入配置项
 * @return {IObj}
 * @example: merge(optionMap, this.options)
 */
export function merge(optMap: IObj, optProp: IObj, key = ''): any {
  if (!isObject(optMap) || !isObject(optProp)) {
    if (key === 'fontSize') {
      // 判断fontSize添加字号缩放：传入配置和全局配置至少有一个明确定义fontSize，否则无法进入判断
      return optProp === undefined
        ? chartFontSize(optMap as unknown as number)
        : typeof optProp == 'number'
        ? chartFontSize(optProp as unknown as number)
        : optProp
    } else if (key === 'color') {
      /*
        判断color：传入配置和全局配置至少有一个明确定义color，否则无法进入判断
        color可能取值：
          1.常规：'#000'
          2.不同系列颜色：['#000', '#00cccc', '#cc0000']
          3.不同系列线向渐变色：
            [
              [['#fff', '#fdc330'], 'top'],
              [['#fff', '#54f2be'], 'top'],
              [['#fff', '#3591fd'], 'top']
            ]
          4.函数：（多用于饼图）
            (params: any) => {
              const colorList = ['#45C2E0', '#C1EBDD', '#FFC851', '#5A5476', '#1869A0']
              return colorList[params.dataIndex]
            }
      */
      const opt = optProp === undefined ? optMap : optProp
      if (toRawType(opt) === 'String' || toRawType(opt) === 'Function') {
        return opt
      } else if (toRawType(opt) === 'Array' && toRawType(opt[0]) === 'Array') {
        return gradientColor(opt[0], opt[1])
      } else {
        console.log('color配置项格式不正确')
      }
    } else {
      return optProp === undefined ? optMap : optProp
    }
  }
  // 合并两个对象的key，区分数组的初始值为[]
  const newOpt = Object.keys({
    ...optMap,
    ...optProp
  }).reduce(
    (acc: IObj, key: string) => {
      // 递归合并
      acc[key] = merge(optMap[key], optProp[key], key)
      return acc
    },
    Array.isArray(optMap) ? [] : {}
  )
  return newOpt
}

/**
 * @name: conver
 * @description: 转换配置项格式（主要用于处理series系列）
 * @param {IObj[]} value 原始数据
 * @param {IObj}   map 数据系列映射
 * @return {IObj[] | undefined}
 * @example: conver(this.options[item], this.map)
 */
export function conver(value: IObj[], map: IObj): IObj[] | undefined {
  // 如果value不存在，则不需要转换处理
  if (!value) return
  const newValue: IObj[] = []
  let index = 0
  const fn = function (origin: IObj, target: IObj) {
    for (const i in origin) {
      // 判断类型：
      // 1.数组：依次取出数组成员赋值对象属性；
      // 2.对象：递归；
      // 3.其余类型(包括函数)：直接赋值不做处理；
      if (toRawType(origin[i]) === 'Array') {
        // 某些配置项本身就可能是数组，不需要转换
        const arr = [
          'data',
          'radius',
          'center',
          'borderRadius',
          'position',
          'offset'
        ]
        if (arr.includes(i)) {
          target[i] = origin[i]
        } else {
          target[i] = origin[i][index]
        }
      } else if (toRawType(origin[i]) === 'Object') {
        target[i] = {}
        fn(origin[i], target[i])
      } else {
        target[i] = origin[i]
      }
    }
  }
  for (const mapKey in map) {
    const obj: IObj = {}
    obj.name = map[mapKey]
    fn(value[0], obj) // 待完善：需判断传入的value是数组or对象
    ++index
    newValue.push(obj)
  }
  return newValue
}

/**
 * @name: deepClone
 * @description: 自定义深拷贝函数
 * @param {any} originValue 原始数据
 * @param {*}   map 弱引用
 * @return {*}
 * @example: const newObj = deepClone(obj)
 */
export function deepClone(originValue: any, map = new WeakMap()): any {
  // 判断Set类型：创建一个新的Set
  if (originValue instanceof Set) {
    return new Set([...originValue])
  }
  // 判断Map类型：创建一个新的Map
  if (originValue instanceof Map) {
    return new Map([...originValue])
  }
  // 判断Symbol类型：创建一个新的Symbol
  if (typeof originValue === 'symbol') {
    return Symbol(originValue.description)
  }
  // 判断函数类型：直接使用同一个函数
  if (typeof originValue === 'function') {
    return originValue
  }
  // 判断对象类型：如果不是对象直接返回本身
  if (!isObject(originValue)) {
    return originValue
  }
  // 判断循环引用
  if (map.has(originValue)) {
    return map.get(originValue)
  }
  // 判断originValue是数组还是对象
  const newObject: any = Array.isArray(originValue) ? [] : {}
  map.set(originValue, newObject)
  for (const key in originValue) {
    newObject[key] = deepClone(originValue[key], map)
  }
  // for循环对象不会遍历到Symbol,此处特殊处理
  const symbolKeys = Object.getOwnPropertySymbols(originValue)
  for (const skey of symbolKeys) {
    newObject[skey] = deepClone(originValue[skey], map)
  }
  return newObject
}
